iT邦幫忙

2022 iThome 鐵人賽

DAY 22
0
Modern Web

擁抱 .Net Core系列 第 22

[Day22] 路由 Routing - 1

  • 分享至 

  • xImage
  •  

一個Http 應用程式可以看做一群endpoint(端點)的集合
透過路由可以將URL配對至對應的對點
讓外部的使用者只要知道URL 就能呼叫需要的端點
Route會把url如何對應endpoint存在路由表中
當request 進來後會根據路由表找尋最佳的的endpoint

使用Route

在pipeline 中使用app.UseRouting(); 可以明確載入路由中介軟體
明確的意思是,如果是WebHost的話,預設就會啟用Routing
但是有時候為了要讓自訂中介軟體在UseRouting()前/後 執行
這時就會明確指定UsingRouting的位置

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.UseRouting();

app.Use(async (context, next) =>
{
    // do something
    await next();
});

app.MapControllers();

await app.RunAsync();

Route Pattern

這邊假設我有個服務可以根據ISBN取得書籍資訊

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.UseRouting();

app.MapGet("/books/{isbn}",
    (string isbn) =>
    {
        return isbn switch
        {
            "1234567890" => Results.Ok(new Book("1234567890", "The Hitchhiker's Guide to the Galaxy")),
            "0987654321" => Results.Ok(new Book("0987654321", "The Restaurant at the End of the Universe")),
            _ => Results.NotFound()
        };
    });

app.MapControllers();

await app.RunAsync();

public record Book(string ISBN, string Name);

當我使用HttpGet呼叫/books/1234567890 可以拿到對應的書籍

其中 /books/{isbn}為這個取得書籍服務endpoint 的路由範本
另外路由範本也可以這樣設定/Homework/{name}.{extension?}
當使用/Homework/SSN1-888.mp4/Homework/SSN1-888
都可以匹配到相同的端點取得檔案

另外也可以使用萬用字元,但只能用在最後一個參數,代表匹配多個路由
/Homework/{*name}
可以用
/Homework/SCD/SSN1/888.mp4
/Homework/Taipei/Hot/n0847.wav
走到相同端點

RoutePatternPart

一個Routing Pattern 可以被拆成RoutePatternPart物件來表示

RoutePatternPart.cs

public abstract class RoutePatternPart
{
    private protected RoutePatternPart(RoutePatternPartKind partKind)
    {
        PartKind = partKind;
    }
    public RoutePatternPartKind PartKind { get; }
    public bool IsLiteral => PartKind == RoutePatternPartKind.Literal;
    public bool IsParameter => PartKind == RoutePatternPartKind.Parameter;
    public bool IsSeparator => PartKind == RoutePatternPartKind.Separator;
    internal abstract string DebuggerToString();
}

RoutePatternPart.cs共有三種類型
由enum RoutePatternPartKind表示
RoutePatternPartKind.cs

public class RoutePatternPartKind{
    Literal,
    Parameter,
    Separator
}
  • Literal: 靜態文字,/books/{isbn}中的books
  • Parameter: 路由參數/books/{isbn}{isbn}/Homework/{*name}{*name} 都是這個類型
  • Separator: /Homework/{name}.{extension?} 中間用分隔符號分開得

其中比較特別的子類別是 RoutePatternParameterPart 也就是參數型態的

public sealed class RoutePatternParameterPart : RoutePatternPart
{
    public IReadOnlyList<RoutePatternParameterPolicyReference> ParameterPolicies { get; }
    public bool EncodeSlashes { get; }
    public object? Default { get; }
    public bool IsCatchAll => ParameterKind == RoutePatternParameterKind.CatchAll;
    public bool IsOptional => ParameterKind == RoutePatternParameterKind.Optional;
    public RoutePatternParameterKind ParameterKind { get; }
    public string Name { get; }
    internal override string DebuggerToString()
}
  • ParameterPolicies: 為路由約束,後面會提到
  • EncodeSlashes: 為true 表示/會被urlencoding成 %2f
  • Default: 假設路由參數有預設值會放在這,book/{isbn = 12},12會放這
  • Name: book/{isbn = 12},Name 為isbn
    ParameterKind.cs
public enum RoutePatternParameterKind
{
    Standard,
    Optional,
    CatchAll,
}

有預設值(book/{isbn = 12})的為Optional,萬用字元則為CatchAll

RoutePattern

我們前面有提到路由表,.Net Core 的路由表本身就是RoutePattern物件的集合

RoutePattern.cs

public sealed class RoutePattern
{
    public IReadOnlyDictionary<string, object?> Defaults { get; }
    public IReadOnlyDictionary<string, IReadOnlyList<RoutePatternParameterPolicyReference>> ParameterPolicies { get; }
    public IReadOnlyDictionary<string, object?> RequiredValues { get; }
    public decimal InboundPrecedence { get; }
    public decimal OutboundPrecedence { get; }
    public string? RawText { get; }
    public IReadOnlyList<RoutePatternParameterPart> Parameters { get; }
    public IReadOnlyList<RoutePatternPathSegment> PathSegments { get; }
}
  • RoutePatternPathSegment: 表示兩個/ 中間的的區段,他是由IReadOnlyList<RoutePatternPart>所組成
  • RawText表示路由字串本身 "book/{isbn = 12}"

InboundPrecedence 與 OutboundPrecedence

不管是針對解析路由,或是產出完整的URL,都會要從路由表找符合的pattern
有沒有那麼一種可能,你要解析的路由剛好滿足兩個或以上的Pattern(有,而且滿常遇到的QQ)
當出現這種形況的時候
InboundPrecedence 表示解析時的加權
OutboundPrecedence 表示產生URL的加權
加權越高,優先度越高


上一篇
[Day20] 中介軟體 Middleware - 3
下一篇
[Day23] 路由 Routing - 2
系列文
擁抱 .Net Core30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言